package com.appkubes.fintech.restful.application.pay.wechat.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.appkubes.fintech.common.exption.BusinessException;
import com.appkubes.fintech.common.response.Response;
import com.appkubes.fintech.restful.application.pay.common.PayConstants;
import com.appkubes.fintech.restful.application.pay.common.contract.response.NotifyResponse;
import com.appkubes.fintech.restful.application.pay.common.contract.response.PayModelResponse;
import com.appkubes.fintech.restful.application.pay.enums.WeChatPayEnums;
import com.appkubes.fintech.restful.application.pay.wechat.WeChatCore;
import com.appkubes.fintech.restful.application.pay.wechat.WeChatPayConstant;
import com.appkubes.fintech.restful.application.pay.wechat.config.WeChatConfig;
import com.appkubes.fintech.restful.application.pay.wechat.request.PayRequest;
import com.appkubes.fintech.restful.application.pay.wechat.service.WeChatPayService;
import com.github.wxpay.sdk.WXPay;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/*
 * 微信接口
 * @author	ocean
 * @date 	2019/07/25 13:53:03
 */
@Service
@Slf4j
public class WeChatPayServiceImpl implements WeChatPayService {

    @Autowired
    private WeChatCore weChatCore;
    @Value("${pay.wechat.appid}")
    private String appId;
    @Value("${pay.wechat.merchid}")
    private String merchId;
    @Value("${pay.wechat.wxkey}")
    private String privateKey;
    @Value("${pay.webchat.domain}")
    private String domain;

    /**
     * 微信h5支付、扫码支付、app支付都是调用此接口
     *
     * @param
     * @param
     * @return 支付Url
     * @throws Exception
     */
    @Override
    public Response<PayModelResponse> doPay(PayRequest payRequest) throws BusinessException {
        try {
            String clientType = "h5";
            if(payRequest.getPayType().equals("2")){
                clientType = "app";
            }
            PayModelResponse payResponse = new PayModelResponse();
            WeChatPayEnums payEnums = WeChatPayEnums.MWEB;
            if (Objects.equals("pc", clientType)) {  // PC端使用，扫码支付
                payEnums = WeChatPayEnums.NATIVE;
            } else if (Objects.equals("app", clientType)) {  // App 支付
                payEnums = WeChatPayEnums.APP;
            } else if (Objects.equals("NWEB", clientType)) {  // App 支付
                payEnums = WeChatPayEnums.MWEB;
            } else if (Objects.equals("h5", clientType)) {  // PC端使用，扫码支付
                payEnums = WeChatPayEnums.MWEB;
            } else if (Objects.equals("JSAPI", clientType)) {  // PC端使用，扫码支付
                payEnums = WeChatPayEnums.JSAPI;
            }
            payResponse.setPayModel(payEnums.getValue()); // 微信支付的类型
            log.info("微信支付 payEnums={}", payEnums.getValue());
            WXPay wxPay = weChatCore.makePay(payEnums);
            Map<String, String> dataMap = weChatCore.makeRequestParams(payEnums, payRequest);
            Map<String, String> resp = wxPay.unifiedOrder(dataMap);
            log.info("resp = {}", resp);
            if (!Objects.equals(resp.get(WeChatPayConstant.RET_CODE), WeChatPayConstant.SUCCESS)) {
                log.info("订单{}微信返回下单失败 ", payRequest.getOrderNo());
                return Response.fail("微信返回下单失败");
            }
            // MWEB_URL后拼接上redirect_url参数，来指定回调页面
            // 微信H5支付scene_info必传
            if(Objects.equals("2",payRequest.getPayType())) {
                payResponse=weChatCore.payRetApp(payResponse,resp);
            }
            else{
                payResponse = weChatCore.payRetH5(payResponse,resp,payRequest);
            }
            return Response.success(payResponse);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BusinessException(e.getMessage());
        }

    }


    /**
     * 支付结果回调
     *
     * @return
     * @throws Exception
     */
    @Override
    public Response<NotifyResponse> notify(HttpServletRequest request, HttpServletResponse response, String orderNo) throws BusinessException {
        log.info("wxNotifyUrl   request={}", request);

        try {
            NotifyResponse notifyResponse = new NotifyResponse();
            notifyResponse.setResponseMeg(WeChatPayConstant.NOTIFY_FAIL);
            Response bizRes = weChatCore.notifyParasm(request);
            if (bizRes.getCode() != 200) {
                log.info(" 微信支付回调： 参数失败");
                return bizRes;
            }
            Map<String, String> notifyMap = (Map<String, String>) bizRes.getData();
            log.info(" 微信支付回调： request={}，params={}", request, JSON.toJSONString(notifyMap));
            //参数交验return_code为SUCCESS的时候有返回
            if (!WeChatPayConstant.SUCCESS.equals(notifyMap.get(WeChatPayConstant.RET_CODE))) {
                notifyResponse.setErrMeg("参数交验失败");
                return Response.fail(notifyResponse);
            }
            //  获取微信的通知返回参数， 商户订单号
            String out_trade_no = notifyMap.get(WeChatPayConstant.NOTIFY_RET_TRADE_NO);
            //微信交易订单号
            String weixin_trade_no = notifyMap.get(WeChatPayConstant.WX_NO);
            //  商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号
//            if (Objects.equals(out_trade_no, orderNo)) {
//                notifyResponse.setErrMeg("数据校验失败");
//                notifyResponse.setResponseMeg(WeChatPayConstant.NOTIFY_FAIL);
//                return Response.fail(notifyResponse);
//            }
            WeChatConfig weChatConfig = new WeChatConfig(appId, merchId, privateKey);
            WXPay wxpay = new WXPay(weChatConfig);
            if (!wxpay.isPayResultNotifySignatureValid(notifyMap)) {  // 签名错误，如果数据里没有sign字段，也认为是签名错误
                log.info("订单 {} 微信支付失败，签名错误 微信通知 notifyData ={}", JSON.toJSONString(notifyMap));
                notifyResponse.setErrMeg("微信支付失败");
                notifyResponse.setResponseMeg(WeChatPayConstant.NOTIFY_FAIL);
                return Response.fail(notifyResponse);
            }

            // 返回状态错误
            if (!WeChatPayConstant.SUCCESS.equals(notifyMap.get("result_code"))) {//交易失败
                log.info("订单 {} 微信支付失败，返回状态错误  微信通知 notifyData ={}", JSON.toJSONString(notifyMap));

                notifyResponse.setErrMeg(JSON.toJSONString(notifyMap));
                notifyResponse.setResponseMeg(WeChatPayConstant.NOTIFY_FAIL);
                return Response.fail(notifyResponse);
            }


            // 注意特殊情况：订单已经退款，但收到了支付结果成功的通知，不应把商户侧订单状态从退款改成支付成功

            notifyResponse.setOutTradeNo(weixin_trade_no);
            notifyResponse.setResponseMeg(WeChatPayConstant.NOTIFY_SUC);
            log.info("微信回调成功...................."+weixin_trade_no);
            return Response.success(notifyResponse);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new BusinessException("非法通知");
        }
    }


    //    /**
//     * 查询微信订单
//     *
//     * @return
//     */
    @Transactional
    @Override
    public Response queryPay(PayRequest payRequest) {
        return weChatTradeQuery(payRequest);
    }
//

    /**
     * @description: 微信公共查询接口
     * @author: xqc
     * @date: 2019/7/18 16:35
     * @params:
     * @return:
     */
    private Response weChatTradeQuery(PayRequest payRequest) {

        String message = "";
        WeChatConfig weChatConfig = new WeChatConfig(appId, merchId, privateKey);
        WXPay wxpay = new WXPay(weChatConfig);
        Map<String, String> reqData = new HashMap<String, String>();
        reqData.put("out_trade_no", payRequest.getOrderNo());
        try {
            Map<String, String> response = wxpay.orderQuery(reqData);
            log.info("resp ={}", response);
            if (!response.get(WeChatPayConstant.RET_CODE).equals(WeChatPayConstant.SUCCESS)) {  //返回状态
                log.info("微信订单{}返回状态为:{}", response.get("result_code"));
                message = "订单" + "微信返回非成功支付状态，状态为：" + response.get("result_code");
                return Response.fail(message);
            }
            if (!response.get(WeChatPayConstant.QUERY_TRADE_STATE).equals(WeChatPayConstant.SUCCESS)) {  // 交易状态
                log.info("微信订单{}返回状态为:{}", response.get(WeChatPayConstant.QUERY_TRADE_STATE));
                message = "订单" + "微信返回非成功支付状态，状态为：" + response.get(WeChatPayConstant.QUERY_TRADE_DES);
                return Response.fail(message);
            }

            return Response.success("订单支付成功，状态已更新请补发通知");

        } catch (Exception e) {
            log.error("微信回调签名认证失败,paramsJson:{},errorMsg:{}", e.getMessage());
            e.printStackTrace();
            throw new BusinessException(e.getMessage());
        }

    }


}
